package com.mike.bdb.util;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.mike.bdb.manager.DBManager;
import com.mike.bdb.ui.provider.tree.TreeSelectionUtil;

public class DPLBDBNameUtil {
	
	private static final Logger LOG = LoggerFactory.getLogger(DPLBDBNameUtil.class);
	
	public static final String BDB_CONFIG_FILE = "src/config.conf";
	
	private static final String CONFIG_MODEL_PATH_KEY = "ModelPath";
	public static final String BERKELEY_DATABASE_CLASS_PATH = "src/com/mike/bdb/model";
	
	private static final String CONFIG_MODEL_PACKAGE_KEY = "PackageName";
	public static final String BERKELEY_DATABASE_CLASS_PREFIX = "com.mike.bdb.model";
	
	private static final String SPLIT = DBManager.BERKELEY_DATABASE_SECONDARY_INDEX_SPLIT_CHAR;
	private static final Map<String, String> configMap = new HashMap<String, String>();
	private static final DPLBDBNameUtil instance = new DPLBDBNameUtil();

	/** 扫描目录 */
	private static volatile String currentScanPath = BERKELEY_DATABASE_CLASS_PATH;
	public static volatile String currentPackageName = BERKELEY_DATABASE_CLASS_PREFIX;
	
	/** 映射方式key:类文件名 value:Class<?> */
	private HashMap<String, Class<?>> models = new HashMap<String, Class<?>>();
	
	private DPLBDBNameUtil() {
		readConfigIfHas();
		dynamicCachedBerkeleyClazz();
	}
	
	private void readConfigIfHas() {
		try {
			File config = Folder.getFile(BDB_CONFIG_FILE);
			BufferedReader br = new BufferedReader(
					new InputStreamReader(new FileInputStream(config), Charset.forName("UTF-8")));
			String temp = "";
			while(null != (temp = br.readLine()) ) {
				readLineForConfig(temp);
			}
			br.close();
			if(LOG.isDebugEnabled()) {
				LOG.debug("Config KV Size:" + configMap.size());
				LOG.debug("Config KV Size:" + configMap.toString());
			}
		} catch (Exception e) {
			//ignore
		}
	}

	private void readLineForConfig(String temp) {
		if(null == temp) return;
		if(temp.startsWith("#")) return;
		if(!temp.contains("=")) return;
		String[] kv = temp.split("=");
		if(kv.length != 2) return;
		String trimK = kv[0].trim();
		String trimV = kv[1].trim();
		if("".equals(trimK) || "".equals(trimV)) return;
		configMap.put(trimK, trimV);
	}

	public Class<?> getClassByName(String name) {
		return models.get(name);
	}
	
	private void dynamicCachedBerkeleyClazz() {
		try {
			setAndSave();
			File file = new File(currentScanPath);
			File[] listFiles = file.listFiles();
			int index = 0;
			for(File modelFile : listFiles) {
				if(modelFile.isDirectory()) continue;
				Class<?> eachBerkeleyDBClazz = Class.forName(currentPackageName + "." + modelFile.getName().replace(".java", ""));
				if (LOG.isDebugEnabled()) {
					LOG.debug("DPL model file name:" + modelFile.getName());
					LOG.debug("DPL model class :" + eachBerkeleyDBClazz);
					LOG.debug("DPL models map cached by [" + eachBerkeleyDBClazz.getName() + "," + eachBerkeleyDBClazz + "]");
				}
				models.put(eachBerkeleyDBClazz.getName(), eachBerkeleyDBClazz);
				index ++;
			}
			if (LOG.isDebugEnabled()) {
				LOG.debug("DPL models size:" + models.size());
			}
			if(index != models.size()) {
				throw new IllegalArgumentException("DPL model class cached error...");
			}
			if(0 == models.size()) {
				LOG.warn("=====================================================");
				LOG.warn(BDB_CONFIG_FILE + " file something error auto reset.");
				LOG.warn("Please restart the programme.");
				LOG.warn("=====================================================");
				Folder.getNewFile(BDB_CONFIG_FILE);
				configMap.put(CONFIG_MODEL_PATH_KEY, BERKELEY_DATABASE_CLASS_PATH);
				configMap.put(CONFIG_MODEL_PACKAGE_KEY, BERKELEY_DATABASE_CLASS_PREFIX);
			}
			saveConfig();
		} catch (Exception e) {
			if(LOG.isErrorEnabled()) {
				LOG.error("Error when cached all model java file.");
				LOG.error(e.getMessage());
			}
		}
	}

	private void setAndSave() {
		if(null == configMap) return;
		String string = configMap.get(CONFIG_MODEL_PATH_KEY);
		if(null != string) {
			if(LOG.isDebugEnabled()) {
				LOG.debug("currentScanPath update from config: " + string);
			}
			currentScanPath = string; 
		} else {
			currentScanPath = BERKELEY_DATABASE_CLASS_PATH;
			configMap.put(CONFIG_MODEL_PATH_KEY, BERKELEY_DATABASE_CLASS_PATH);
		}
		string = configMap.get(CONFIG_MODEL_PACKAGE_KEY);
		if(null != string) {
			if(LOG.isDebugEnabled()) {
				LOG.debug("packeage update from config: " + string);
			}
			currentPackageName = string; 
		} else {
			currentPackageName = BERKELEY_DATABASE_CLASS_PREFIX; 
			configMap.put(CONFIG_MODEL_PACKAGE_KEY, BERKELEY_DATABASE_CLASS_PREFIX);
		}
		
	}
	
	public static DPLBDBNameUtil getInstance() {
		return instance;
	}
	
	public KeyName getKeyNameByDatabaseName(String databaseName) {
		if(null == databaseName || "".equals(databaseName.trim())) {
			return null;
		}
		databaseName = TreeSelectionUtil.getDatabaseClassName(databaseName);
		String[] split = databaseName.split("\\.");
		String className = split[split.length - 1];
		KeyName keyName = null;
		if (className.contains(SPLIT)) {
			keyName = new KeyName(className.split(SPLIT)[0],
					className.split(SPLIT)[1], 
					currentPackageName + "." + className.split(SPLIT)[0]);
		} else if (className.equals(DataCurrent.getInstance().curDataBasePath)) {
			return null;
		} else {
			keyName = new KeyName(
					className, className,
					currentPackageName + "." + className.split(SPLIT)[0]);
		}
		return keyName;
	}

	public void saveConfig() {
		File file = Folder.getFile(BDB_CONFIG_FILE);
		try {
			PrintWriter pw = new PrintWriter(file);
			Iterator<Entry<String, String>> iterator = configMap.entrySet().iterator();
			pw.append("# you can modify below properties when error auto reset.");
			pw.append("\r\n");
			while(iterator.hasNext()) {
				Entry<String, String> next = iterator.next();
				String key = next.getKey();
				String value = next.getValue();
				pw.append(key);
				pw.append("=");
				pw.append(value);
				pw.append("\r\n");
			}
			pw.flush();
			pw.close();
		} catch (FileNotFoundException e) {
			//ignore.
		}
	}
	
	public static void main(String[] args) {
		DPLBDBNameUtil instance = DPLBDBNameUtil.getInstance();
		instance.saveConfig();
	}
	
	public KeyName newKeyName(String prefix, String suffix, String curStoreClazz) {
		return new KeyName(prefix, suffix, curStoreClazz);
	}
	
	public class KeyName {
		
		/** 类名 */
		private String prefix;
		/** 二级索引 */
		private String suffix;
		/** 对象类名 */
		private String curStoreClazz;
		
		public KeyName(String prefix, String suffix, String curStoreClazz) {
			this.prefix = prefix;
			this.suffix = suffix;
			this.curStoreClazz = curStoreClazz;
		}
		
		public boolean isSec() {
			return !prefix.endsWith(suffix);
		}
		
		public String getPrefix() {
			return prefix;
		}
		
		public String getSuffix() {
			return suffix;
		}
		
		public String getCurStoreClazz() {
			return curStoreClazz;
		}
		
	}
}
